﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLite.Domain.Model;
using NLite.Data;
using System.Linq.Expressions;

namespace NLite.Domain.Repository
{
    /// <summary>
    /// 仓储接口
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    [Contract]
    public interface IRepository<TModel, TId>
        where TModel : class,IEntity<TId>
    {
        /// <summary>
        /// 向仓储中添加记录
        /// </summary>
        /// <param name="model"></param>
        void Add(TModel model);
        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="ids"></param>
        void Delete(params TId[] ids);
        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="model"></param>
        void Update(TModel model);
        /// <summary>
        /// 通过标志获取
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        TModel Get(TId id);
        /// <summary>
        /// 查询所有记录
        /// </summary>
        /// <returns></returns>
        IQueryable<TModel> Query();

        /// <summary>
        /// 分页、搜索、排序查询
        /// </summary>
        /// <param name="ctx"></param>
        /// <returns></returns>
        IPagination<TModel> Query(QueryContext ctx);
    }

    /// <summary>
    /// 仓储接口
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    [Contract]
    public interface IRepository<TModel> : IRepository<TModel, long>
        where TModel :class, IEntity<long>
    {
    }

    public abstract class Repository<TModel, TId> : IRepository<TModel, TId> where TModel : class,IEntity<TId>
    {
        private static readonly ParameterExpression modelParameter = Expression.Parameter(typeof(TModel),"p");
        private static readonly MemberExpression idMemberExpression = Expression.Property(modelParameter, "Id");

        public abstract void Add(TModel model);

        public virtual void Delete(params TId[] ids)
        {
            Guard.NotNull(ids,"ids");

            var argExpression = Expression.Constant(ids);
            var containExp = Expression.Call(
                                            typeof(Enumerable)
                                            , "Contains"
                                            , new[] { typeof(TId) }
                                            , argExpression
                                            , idMemberExpression);

            var containLambda = LambdaExpression.Lambda<Func<TModel,bool>>(containExp, modelParameter);

            var deleted = Query().Where(containLambda).ToArray();
            foreach (var item in deleted)
                Delete(item);
        }

        public abstract void Delete(TModel model);

        public abstract void Update(TModel model);

        public virtual TModel Get(TId id)
        {
            var argExpression = Expression.Constant(id, typeof(TId));
            var eqExpression = Expression.Equal(idMemberExpression, argExpression);
            var predExp = LambdaExpression.Lambda<Func<TModel, bool>>(eqExpression,modelParameter);
            return Query().FirstOrDefault(predExp);
        }

        public abstract IQueryable<TModel> Query();

        public abstract IPagination<TModel> Query(QueryContext ctx);
    }
}
